package com.dannypatterson.as3p2p {
	
	import com.dannypatterson.as3p2p.events.ConnectionEvent;
	import com.dannypatterson.as3p2p.events.IncomingConnectionEvent;
	
	import flash.errors.IllegalOperationError;
	import flash.events.ErrorEvent;
	import flash.events.EventDispatcher;
	import flash.events.NetStatusEvent;
	import flash.net.NetConnection;
	import flash.net.NetStream;
	import flash.net.registerClassAlias;
	
	import mx.rpc.events.FaultEvent;
	
	/**
	 * this is the main class that holds the connect with stratus.  It listens
	 * for incoming connection requests from other peers and allows you to make
	 * connection requests to other users.
	 * 
	 * @author Danny Patterson
	 */
	public class ConnectionManager extends EventDispatcher {
		
		private var key:String;
		private var stratusConnection:NetConnection;
		private var responderStream:NetStream;
		private var _me:Peer;
		
		/**
		 * @return (Peer) Your peer object.
		 */
		public function get me():Peer {
			return _me;
		}
		
		public function get connected():Boolean {
			return stratusConnection != null && stratusConnection.connected;
		}
		
		/**
		 * @param key (String) The stratus API key used to connect.
		 */
		public function ConnectionManager(key:String) {
			this.key = key;
			registerClassAlias("com.dannypatterson.as3p2p.Peer", Peer);
		}
		
		
		private function onConnectionStatus(event:NetStatusEvent):void {
			switch(event.info.code) {
				case "NetConnection.Connect.Success":
					_me = new Peer();
					_me.id = stratusConnection.nearID;
					responderStream = new NetStream(stratusConnection, NetStream.DIRECT_CONNECTIONS);
					var c:Object = new Object();
					// This is called when someone connects our "listener" stream; we interpret this as a request to connect
					c.onPeerConnect = function(caller:NetStream):Boolean {
						dispatchEvent(new IncomingConnectionEvent(IncomingConnectionEvent.INCOMING_CONNECTION, caller.farID));
						return true;
					}
					responderStream.client = c;
					responderStream.publish("listener");
					dispatchEvent(new ConnectionEvent(ConnectionEvent.CONNECTED));
					break;
				case "NetConnection.Connect.Failed":
					dispatchEvent(new ErrorEvent(ErrorEvent.ERROR, false, false, "You failed to connect to the Stratus service."));
					break;
				case "NetStream.Connect.Closed":
					// TODO: Re-establish the connection
					break;
			}
		}
		
		
		/**
		 * This method initiates a connection with the Stratus service.
		 */
		public function connect():void {
			stratusConnection = new NetConnection();
			stratusConnection.addEventListener(NetStatusEvent.NET_STATUS, onConnectionStatus, false, 0, true);
			stratusConnection.connect("rtmfp://stratus.adobe.com/" + key);
		}
		
		public function disconnect():void {
			stratusConnection.close();
		}
		
		/**
		 * This method is used to accept a conection request from another user.
		 * 
		 * @return (PeerConnection) The connection with the peer.
		 */
		public function acceptConnection(peer:Peer):PeerConnection {
			var channel:String = "channel_" + Math.round(Math.random() * new Date().valueOf());
			responderStream.send("onConnectionAccepted", me, channel);
			var peerConnection:PeerConnection = new PeerConnection(stratusConnection, me, peer, channel);
			return peerConnection;
		}
		
		/**
		 * This method is used to request a connection with another peer.
		 * 
		 * @return (OutgomingConnector) The outgoing connector used to make the request to the peer.
		 */
		public function connectToPeer(peer:Peer):OutgoingConnector {
			if(peer.id == me.id) {
				throw new IllegalOperationError("You can't connect to yourself");
			}else {
				var outgoingConnector:OutgoingConnector = new OutgoingConnector(stratusConnection, peer);
				outgoingConnector.requestConnection();
				return outgoingConnector;
			}
		}
		
		/**
		 * This method is used to create a PeerConnection
		 * 
		 * @param peer (Peer) 
		 * @param channel (String) 
		 */
		public function createPeerConnection(peer:Peer, channel:String):PeerConnection {
			return new PeerConnection(stratusConnection, me, peer, channel);
		}

	}
	
}